package HTML::FormFu::Element::DBIC::Select;

use strict;
use warnings;

use base 'HTML::FormFu::Element::Select';

use Carp qw/ croak /;


__PACKAGE__->mk_accessors( qw/
    schema
    model
    search
    where
    search_attr
    name_field
    value_field
    sort
    sort_field
    localize_name
/ );

*resultset = \&model;

sub process {
    my $self = shift;

    $self->_add_options;

    my $process = $self->next::method( { @_ ? %{ $_[0] } : () } );

    return $process;
}


sub render {
    my $self = shift;

    $self->_add_options;

    my $render = $self->next::method( { @_ ? %{ $_[0] } : () } );

    return $render;
}


sub _add_options {
    my $self = shift;

    # shortcut
    if (defined $self->{__data}) {
        $self->options( $self->{__data} );
        return;
    }

    # get stash
    my $stash = $self->form->{stash};

    # get resultset object
    my $resultset;
    if ( $stash->{context} && $self->model ) {
        # use Catalyst context with model name

        $resultset = $stash->{context}->model( $self->model )
            or croak "Model '". $self->model. "' could not be loaded.";
    }
    elsif ( $stash->{schema} && $self->resultset ) {
        # use DBIC schema with resultset name

        my $schema = $stash->{schema};
        $resultset = $schema->resultset( $self->resultset )
            or croak "Resulset '". $self->resultset. "' could not be loaded.";
    }
    else {
        croak "need a Catalyst context with a model name or ".
            "a DBIC schema with the name of a resultset in the stash\n";
    }

    # get data
    my @data;
    if (!defined $self->search() && !defined $self->where() && !defined $self->search_attr()) {
        # we just wan't all of it
        @data = $resultset->all();
    }
    else {
        # just some data
        my $search = $self->search() || 'search';
        my $where = $self->where() || {};
        my $search_attr = $self->search_attr() || {};

        @data = $resultset->$search( $where, $search_attr );
    }

    # get name, value and sort fields
    my $name_field = $self->name_field  || $self->name;
    my $value_field = $self->value_field || 'id';
    my $sort_field = $self->sort_field || $name_field;

    # create array of options
    my $options;

    # sort data if defined
    my $sort = $self->sort();
    if ( $sort ) {
        if ( lc($sort ) eq "desc" ) {
            # $sort = "desc";
            $options = [
                map { [ $_->[0], $_->[1] ] }
                sort { $b->[2] cmp $a->[2] }
                map { [ $_->$value_field, $_->$name_field, $_->$sort_field ] }
                @data
            ];
        }
        else {
            # $sort = "asc"
            $options = [
                map { [ $_->[0], $_->[1] ] }
                sort { $a->[2] cmp $b->[2] }
                map { [ $_->$value_field, $_->$name_field, $_->$sort_field ] }
                @data
            ];
        }
    }
    else {
        $options = [ map { [ $_->$value_field, $_->$name_field ] } @data ];
    }

    # transform to hash form if loc_label is defined
    if ( $self->localize_name() ) {
        $options =
            map { { value => $_->[0], label_loc => $_->[1] } }
            @$options;
    }

    # safe for shortcut
    $self->{__data} = $options;

    # set array of options
    $self->options( $options );

    return;
}


1;

__END__

=head1 NAME

HTML::FormFu::Element::DBIC::Select - DBIC driven Select Element

=head1 DESCRIPTION

Gets it's data from DBIC model in Catalyst context or DBIC Schema.

Parameters:
    model - name of the model
      (will be used in Catalyst context)
    resultset - name of the resultset in schema to use
      (schema expected in the stash as 'schema')
    search - name of search function to use
      (default is 'search')
    where - where structure, as expected from DBIC::Resulset::search
      (default is none)
    search_attr - structure, as expected from DBIC::Resulset::search
      (default is none)
    name_field - will be used as the name of the option elements.
    value_field  - will be used as the value for the option elements.
      (default is 'id')
    sort - either 'asc' or 'desc' if the data shall be ordered
      The ordering is done alphabetically (cmp).
      If you want to use an other field for sorting use sort_field
    sort_field - will be used as column name for sorting order (see 'sort')
      (defaults to name_field)
    localize_name - if set to a true value the labels will be passed through
      the I18N module to translate it

=head1 HOWTO

=head2 create element

Example:
      - type: HTML::FormFu::Element::DBIC::Select
        model: 'glueDB::Contact::Channel::Entry::Type'
        name: type_id
        label: Channel Type
        search: my_custom_search
        name_field: name
        value_field: id
        constraints: [ AutoSet ]
        sort_field: name_to_sort
        sort: desc
        where:
          -or:
            - -and
            -
              - artist
              - like: '%Smashing Pumpkins%'
              - title
              - Siamese Dream
            - artist
            - Starchildren
        search_attr:
          as:
            - name
            - cd_count
          group_by:
            - name
          join:
            - cds
          select:
            - name
            - count: cds.cdid

=head2 create where and attributes clauses

There is support for complex where clauses and attributes.

Here you find some examples of how you transform the Perl structures into
something YAML understands:

my $where_hash = {
    -or => [
      -and => [
        artist => { 'like', '%Smashing Pumpkins%' },
        title  => 'Siamese Dream',
      ],
      artist => 'Starchildren',
    ],
  };

would be in YAML:
where:
  -or:
    - -and
    -
      - artist
      - like: '%Smashing Pumpkins%'
      - title
      - Siamese Dream
    - artist
    - Starchildren


my $another_where_hash = {
    artist => { 'like', '%Lamb%' },
    title  => { 'like', '%Fear of Fours%' },
  };

would be in YAML:
where:
  artist:
    like: '%Lamb%'
  title:
    like: '%Fear of Fours%'


my $attribute_hash =     {
      join     => [qw/ cds /],
      select   => [ 'name', { count => 'cds.cdid' } ],
      as       => [qw/ name cd_count /],
      group_by => [qw/ name /]
    };

would be in YAML:
search_attr:
  as:
    - name
    - cd_count
  group_by:
    - name
  join:
    - cds
  select:
    - name
    - count: cds.cdid

=head1 KNOWN PROBLEMS
If a custom search function is used the function will also get the where and
search_attr as parameters.

=head1 SEE ALSO

Is a sub-class of, and inherits methods from L<HTML::FormFu::Element::Select>

L<HTML::FormFu::FormFu>

=head1 AUTHOR

Mario Minati C<mario.minati@googlemail.com>

=head1 LICENSE

This library is free software, you can redistribute it and/or modify it under
the same terms as Perl itself.
